#include "assignment2.h"
#include "system.h"



void InitializeReceptionist(int num){
    NumberOfReceptionist = num;
    Token = 1;

    ReceptionistQueue = new int[NumberOfReceptionist+1];

    ReceptionistQueue = new int[NumberOfReceptionist+1];
    ReceptionistQueueLock = new char[50];
    sprintf(ReceptionistQueueLock,"ReceptionistQueueLock");
    CreateLock(ReceptionistQueueLock);

    ReceptionistQueueCV = new char*[NumberOfReceptionist+1];
    for(int i=1; i<=NumberOfReceptionist; i++){
    	ReceptionistQueueCV[i] = new char[50];
        sprintf(ReceptionistQueueCV[i],"ReceptionistQueueCV_%d",i);
        CreateCondition(ReceptionistQueueCV[i]);
    }

    PatientReceptionistLock = new char*[NumberOfReceptionist+1];
    for(int i=1; i<=NumberOfReceptionist; i++){
    	PatientReceptionistLock[i] = new char[50];
        sprintf(PatientReceptionistLock[i],"PatientReceptionistLock_%d",i);
    }

    PatientReceptionistCV = new char*[NumberOfReceptionist+1];
    for(int i=1; i<=NumberOfReceptionist; i++){
    	PatientReceptionistCV[i] = new char[50];
        sprintf(PatientReceptionistCV[i],"PatientReceptionistCV_%d",i);
    }

    ReceptionistTokenLock = new char[50];
    sprintf(ReceptionistTokenLock,"ReceptionistTokenLock");
    CreateLock(ReceptionistTokenLock);

}

void InitializeDoctor(int num){
    NumberOfDoctors = num;
    
    DoctorQueue = new int[NumberOfDoctors+1];
    PatientDoctorBuffer = new int[NumberOfDoctors+1];
    DoctorList = new int[NumberOfDoctors+1];

    for(int i=1; i<=NumberOfDoctors; i++){
        DoctorList[i]=0;
    }

    DoctorQueueLock = new char[50];
    sprintf(DoctorQueueLock,"DoctorQueueLock");
    CreateLock(DoctorQueueLock);

    DoctorListLock = new char[50];
    sprintf(DoctorListLock,"DoctorListLock");
    CreateLock(DoctorListLock);

    DoctorQueueCV = new char*[NumberOfDoctors+1];
    for(int i = 1; i <= NumberOfDoctors; i++){
    	DoctorQueueCV[i] = new char[50];
        sprintf(DoctorQueueCV[i],"DoctorQueueLock_%d",i);
        CreateCondition(DoctorQueueCV[i]);
    }

    PatientDoctorCV = new char*[NumberOfDoctors+1];
    for(int i = 1; i <= NumberOfDoctors; i++){
    	PatientDoctorCV[i] = new char[50];
        sprintf(PatientDoctorCV[i],"PatientDoctorCV_%d",i);
    }

    PatientDoctorLock = new char*[NumberOfDoctors+1];
    for(int i = 1; i <= NumberOfDoctors; i++){
    	PatientDoctorLock[i] = new char[50];
        sprintf(PatientDoctorLock[i],"PatientDoctorLock_%d",i);
    }
    
    DoctorDoorBoyLock = new char*[NumberOfDoctors+1];
    for(int i = 1; i <= NumberOfDoctors; i++){
    	DoctorDoorBoyLock[i] = new char[50];
        sprintf(DoctorDoorBoyLock[i],"DoctorDoorBoyLock_%d",i);
        CreateLock(DoctorDoorBoyLock[i]);
    }

    DoctorDoorBoyCV = new char*[NumberOfDoctors+1];
    for(int i = 1; i <= NumberOfDoctors; i++){
    	DoctorDoorBoyCV[i] = new char[50];
        sprintf(DoctorDoorBoyCV[i],"DoctorDoorBoyCV_%d",i);
        CreateLock(DoctorDoorBoyCV[i]);
    }

    DoctorCV = new char*[NumberOfDoctors+1];
    for(int i = 1; i <= NumberOfDoctors; i++){
    	DoctorCV[i] = new char[50];
        sprintf(DoctorCV[i],"DoctorCV_%d",i);
        CreateLock(DoctorCV[i]);
    }
}

void Patient(int PatientId){
    int ReceptionistId;
    int DoctorId;
    int PatientToken;
    int PatientPrescription;

    /* --------------------------
     * PATIENT RECEPTION INTERACTION
     * --------------------------
     */

    // Get queue lock to inter the line
    Acquire(ReceptionistQueueLock);
    ReceptionistId = ShortestQueue(ReceptionistQueue, NumberOfReceptionist);
    ReceptionistQueue[ReceptionistId]++;

    // wait on condition for receptionist to respond
    Wait(ReceptionistQueueCV[ReceptionistId],ReceptionistQueueLock);

    //leave the queue to interact the receptionist
    ReceptionistQueue[ReceptionistId]--;
    Release(ReceptionistQueueLock);

    //acquire the receptionist interaction lock
    Acquire(PatientReceptionistLock[ReceptionistId]);

    //signal the waiting receptionist on the condition
    Signal(PatientReceptionistCV[ReceptionistId],
            PatientReceptionistLock[ReceptionistId]);

    //wait for receptionist to pass the token
    Wait(PatientReceptionistCV[ReceptionistId],
            PatientReceptionistLock[ReceptionistId]);

    //keep token in local variable for future use
    PatientToken = PatientReceptionistBuffer;

    //Signal the receptionist the token is written and leave the lock
    Signal(PatientReceptionistCV[ReceptionistId],
            PatientReceptionistLock[ReceptionistId]);

    //leaving receptionist window
    Release(PatientReceptionistLock[ReceptionistId]);

    /* --------------------------
     * PATIENT DOCTOR INTERACTION
     * --------------------------
     */

    //Patient gets into Random Doctor queue
    DoctorId = RandomNumber(1, NumberOfDoctors);

    //Get the lock to the Queue to enter
    Acquire(DoctorQueueLock);

    //Icreament size of queue
    DoctorQueue[DoctorId]++;

    //Waits on condition for door boy to signal
    Wait(DoctorQueueCV[DoctorId], DoctorQueueLock);

    //Leaves the queue after door boy signal
    DoctorQueue[DoctorId]--;

    //Release the queue lock
    Release(DoctorQueueLock);

    //The above doesnt get signalled till doctor in wait
    //Get lock and signal doctor
    Acquire(PatientDoctorLock[DoctorId]);

    //Passing the patient token
    PatientDoctorBuffer[DoctorId] = PatientToken;

    //Signals the Doctor with token number
    Signal(PatientDoctorCV[DoctorId],
            PatientDoctorLock[DoctorId]);

    //Wait for the doctor to give the prescription
    Wait(PatientDoctorCV[DoctorId],
            PatientDoctorLock[DoctorId]);

    PatientPrescription = PatientDoctorBuffer[DoctorId];


    //Tell the doctor THANKS and release lock for further interaction
    Signal(PatientDoctorCV[DoctorId],
            PatientDoctorLock[DoctorId]);

    Release(PatientDoctorLock[DoctorId]);

}

void Receptionist(int ReceptionistId){
    while(1){
        //Get line lock to check the size
        Acquire(ReceptionistQueueLock);

        //If size zero go on a break

        if(ReceptionistQueue[ReceptionistId]==0){
            /********************
             * WRITE CODE LATER *
             ********************/ 
            continue;
        }

        //Create the new locks for interaction with the patient
        CreateLock(PatientReceptionistLock[ReceptionistId]);
        CreateCondition(PatientReceptionistCV[ReceptionistId]);

        //Call the waiting patient in the queue on the Condition
        Signal(ReceptionistQueueCV[ReceptionistId],
                ReceptionistQueueLock);

        //Acquire the patient interaction lock create before
        Acquire(PatientReceptionistLock[ReceptionistId]);
        Release(ReceptionistQueueLock);
        //Release the Queue lock later for synchronization

        //Wait for the patient to respond
        Wait(PatientReceptionistCV[ReceptionistId],
                PatientReceptionistLock[ReceptionistId]);

        //Release the token from the global variable by locking to for access
        //and modification
        Acquire(ReceptionistTokenLock);
        PatientReceptionistBuffer = Token++;
        Release(ReceptionistTokenLock);

        //Signal the patient for available token
        Signal(PatientReceptionistCV[ReceptionistId],
                PatientReceptionistLock[ReceptionistId]);

        //Wait for patient to give confirmation before leaving the lock
        Wait(PatientReceptionistCV[ReceptionistId],
                PatientReceptionistLock[ReceptionistId]);

        //Release lock and Destroy them
        Release(PatientReceptionistLock[ReceptionistId]);

        DestroyCondition(PatientReceptionistCV[ReceptionistId]);
        DestroyLock(PatientReceptionistLock[ReceptionistId]);
    }
}

void Doctor(int DoctorId){
    int localToken;

    while(1){

        //Create the patient doctor inteaction lock and
        CreateLock(PatientDoctorLock[DoctorId]);
        CreateCondition(PatientDoctorCV[DoctorId]);

        //Acquires list lock for door boy assignment
        Acquire(DoctorListLock);

        //if no door boy for the doctor
        if(DoctorList[DoctorId]==0){
            //Wait for the door boy to be assigned
            Wait(DoctorCV[DoctorId], DoctorListLock);
        }

        //Release the list lock for others use
        Release(DoctorListLock);

        //Get the interaction lockl for the doorboy
        Acquire(DoctorDoorBoyLock[DoctorId]);

        //Get the interaction lock for the patient
        Acquire(PatientDoctorLock[DoctorId]);

        //Signal the door boy to send the patient
        Signal(DoctorDoorBoyCV[DoctorId],
                DoctorDoorBoyLock[DoctorId]);

        //Wait for the patient to arrive
        Wait(PatientDoctorCV[DoctorId],
                PatientDoctorLock[DoctorId]);

        //get the local token number and give prescription
        for(int i=0; i<5; i++)
            Yield();
        localToken = PatientDoctorBuffer[DoctorId];
        PatientDoctorBuffer[DoctorId] = RandomNumber(10,20);

        //signal the waiting patient with the info and
        // wait for if to inform
        Signal(PatientDoctorCV[DoctorId],
                PatientDoctorLock[DoctorId]);

        Wait(PatientDoctorCV[DoctorId],
                PatientDoctorLock[DoctorId]);

        //interaction complete release the lock and the destroy them
        Release(PatientDoctorLock[DoctorId]);

        DestroyCondition(PatientDoctorCV[DoctorId]);
        DestroyLock(PatientDoctorLock[DoctorId]);

        if(RandomNumber(0,10)>2){
            for(int i=0; i<10; i++)
                Yield();
        }

        //signal the door boy to be free
        Signal(DoctorDoorBoyCV[DoctorId],
                DoctorDoorBoyLock[DoctorId]);
        //release lock
        Release(DoctorDoorBoyLock[DoctorId]);
    }
}

void DoorBoy(int DoorBoyId){
    int DoctorId=0;

    while(1){
        Acquire(DoctorQueueLock);
        Acquire(DoctorListLock);
        if(DoctorId==0){
            for(int i=1; i<=NumberOfDoctors; i++){
                if(DoctorList[i]==0 && DoctorQueue[i]>0){
                    DoctorId = i;
                    DoctorList[i]=1;
                    Signal(DoctorCV[DoctorId], DoctorListLock);
                    break;
                }
            }
        }
        if(DoctorId==0){
            Release(DoctorQueueLock);
            Release(DoctorListLock);
            //Go on a break;
            continue;
        } else {

            Acquire(DoctorDoorBoyLock[DoctorId]);

            Release(DoctorListLock);

            Wait(DoctorDoorBoyCV[DoctorId],
                    DoctorDoorBoyLock[DoctorId]);

            Signal(DoctorQueueCV[DoctorId],
                    DoctorQueueLock);

            Release(DoctorQueueLock);

            Wait(DoctorDoorBoyCV[DoctorId],
                    DoctorDoorBoyLock[DoctorId]);

            Release(DoctorDoorBoyLock[DoctorId]);


            Acquire(DoctorQueueLock);

            if(DoctorQueueLock[DoctorId]==0){
                Acquire(DoctorListLock);
                DoctorList[DoctorId]=0;
                DoctorId=0;
                Release(DoctorListLock);
                Release(DoctorQueueLock);
                //Going on break
                continue;
            }

            Release(DoctorQueueLock);
        }
    }
}

int ShortestQueue(int Queue[], int QueueSize){
    int retValue=-1;
    int longest=-1;

    for(int i = 1; i <= QueueSize; i++){
        if(longest < Queue[i]){
            longest = Queue[i];
            retValue = i;
        }
    }
    return retValue;
}


